;*******************************************************************************
; Director - Memoriser
;
; Copyright (C) 2003, Nick Craig-Wood and Philip Ludlam
;
;This program is free software; you can redistribute it and/or modify it under
;the terms of the GNU General Public License as published by the Free Software
;Foundation; either version 2 of the License, or (at your option) any later
;version.
;
;This program is distributed in the hope that it will be useful, but WITHOUT ANY
;WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
;PARTICULAR PURPOSE. See the GNU General Public License for more details.
;
;You should have received a copy of the GNU General Public License along with
;this program; if not, write to the Free Software Foundation, Inc., 59 Temple
;Place - Suite 330, Boston, MA 02111-1307, USA
;
;*******************************************************************************
;----h- Director.s.Memoriser
; Name
;   Memoriser
;
; Purpose
;   Memoriser
;------
;*******************************************************************************


		TTL	> Memoriser

		GET	OSLib:oslib.hdr.Wimp
		GET	OSLib:oslib.hdr.Filter
		GET	OSLib:oslib.hdr.Filer
		GET	OSLib:oslib.hdr.OSFile
		GET	OSLib:oslib.hdr.OSFSControl
		GET	AsmLib2:hdr.RegsBoth
		GET	AsmLib2:hdr.MacrosBoth
		GET	h.WorkSpace
		GET	h.ListMacros
		GET	h.Constants
		GET	h.Memory
		GET	h.Task
		GET	h.Menus
		GET	h.BMG
		GET	h.ModuleHead

		AREA	|Memoriser|, CODE, READONLY


;*******************************************************************************
;Variables local to the Memoriser
;These are allocated from a block in the main workspace
;*******************************************************************************

			^	(:INDEX: MemoriserVars), wp

MemorisedFiles		#	StringBlock
MaxMemoriserItems	#	4
MaxMemoriserItemsPerMenu	#	4
WimpScrap		#	4

			ASSERT	@ <= EndMemoriserVars


;*******************************************************************************
;----f- Director.s.Memoriser.MemoriserInitialise
; Name
;   MemoriserInitialise
;
; Purpose
;   This initialises the Memoriser
;
; Entry
;
;
; Exit
;   It may return an error
;------
;*******************************************************************************


MemoriserInitialise	ROUTINE	"r0-r1", EXPORT

		ADR	r0, WimpScrapVar
		BL	Canonicalise
		BVS	error$l
		MOV	r0, r1				; r0 = size required for filename
		BL	malloc				; allocate memory
		MOVVS	r0, #0
		STR	r0, WimpScrap			; store ptr to memory
		BVS	error$l
		MOV	r1, r0				; to
		ADR	r0, TempSpace			; from
		BL	strcpy				; copy the string

		ClearV
error$l		EXIT


;*******************************************************************************
;----f- Director.s.Memoriser.MemoriserFinalise
; Name
;   MemoriserFinalise
;
; Purpose
;   This finalises the Memoriser
;
; Entry
;   none
;
; Exit
;   none
;------
;*******************************************************************************


MemoriserFinalise	ROUTINE	"r0", EXPORT

		LDR	r0, MemorisedFiles + StringBlock_buffer ; free StringBlock
		BL	free
		LDR	r0, WimpScrap			; free WimpScrap
		BL	free

		EXIT


;*******************************************************************************
;----f- Director.s.Memoriser.MemoriserFilter
; Name
;   MemoriserFilter
;
; Purpose
;   Filter Handler
;
; Entry
;   r0 = event reason code
;   r1  event block as returned from Wimp_Poll
;   r2 = task handle of task being returned to
;   wp = as setup
;
; Exit
;   r0 modified event code or -1 to claim the call
;------
;*******************************************************************************


MemoriserFilter	ROUTINE	"r0-r11", EXPORT

		MOV	r11, r1				; r11  Poll block

		LDR	r9, [r11, #Wimp_MessageHeader_action] ; r9 = message type
		CMP	r9, #Message_DataSaveAck
		CMPNE	r9, #Message_DataLoad
		CMPNE	r9, #Message_DataLoadAck
		CMPNE	r9, #Message_DataOpen
		ADDEQ	r8, r11, #Wimp_MessageHeader_data + Wimp_MessageDataXfer_file_name
		BEQ	ok$l				; found DataXfer message

		CMP	r9, #Message_FilerOpenDir
		ADDEQ	r8, r11, #Wimp_MessageHeader_data + Filer_MessageOpenDir_dir_name
		ADDNE	r8, r11, #Wimp_MessageHeader_data + Filer_MessageOpenDirAt_dir_name ; r8  directory name
		LDRNE	lr, =Message_FilerOpenDirAt
		CMPNE	r9, lr
		BNE	exit$l				; only OpenDir or OpenDirAt
ok$l

		MOV	r0, r8
		BL	MemoriseFile
		BLVS	Alert

exit$l		ClearFlags
		EXIT


;*******************************************************************************
;----f- Director.s.Memoriser.find_previous_string
; Name
;   find_previous_string
;
; Purpose
;   This finds the previous string
;
; Entry
;   r8  start of string block
;   r9  current string
;
; Exit
;   r9  previous string
;------
;*******************************************************************************


find_previous_string	ROUTINE

find$l		CMP	r8, r9				; if r8 = r9 then is found
		EXIT	EQ

		LDRB	r0, [r9, #-1]
		CMP	r0, #32				; if r0 < 32
		EXIT	LT

		SUB	r9, r9, #1			; point to previous byte
		B	find$l


;*******************************************************************************
;----f- Director.s.Memoriser.delete_string
; Name
;   delete_string
;
; Purpose
;   This deletes a string from the block
;
; Entry
;   r4  StringBlock
;   r5 = delete last item (0 or 1)
;   r6 = item count
;   r8  start of string block
;   r9  current string
;
; Exit
;   EQ => string not deleted because it was the last in the block
;------
;*******************************************************************************


delete_string	ROUTINE

		MOV	r0, r9
		BL	strlen1
		MOV	r3, r0				; r3 = length of string + 0

		SUB	r0, r9, r8			; r0 = offset to start of string
		ADD	r1, r0, r3			; r1 = offset to start of next string

		LDR	lr, [r4, #StringBlock_offset]
		SUBS	r2, lr, r1			; r2 = length of data to move
		CMPEQ	r5, #0				; if we are not to delete last item
		EXIT	EQ				; and if this is already the last string then exit
		SUB	lr, lr, r3			; take the length of the string off the block
		STR	lr, [r4, #StringBlock_offset]

		ADD	r0, r9, r3			; from the start of the next string
		MOV	r1, r9				; to the current string position
		BL	memcpy				; length r2

		SUB	r6, r6, #1			; one less string
		EXIT					; return with flags (NE)


;*******************************************************************************
;----f- Director.s.Memoriser.MemoriseMenuMenu
; Name
;   MemoriseMenuMenu
;
; Purpose
;   MemoriseMenuMenu
;
; Entry
;   r0  filename to memorise
;
; Exit
;
;------
;*******************************************************************************


MemoriseMenuMenu	ROUTINE "r0", EXPORT

;		When the rest has been implemented ...
;		LDR	lr, OptionMemoriseMenuMenu
;		CMP	lr, #0
;		BNE	exit$l

		ADR	r0, newpath$l
		BL	Canonicalise
		ADR	r0, TempSpace
		BL	MemoriseFile
		BLVS	Alert

exit$l		EXIT

newpath$l	DCB	"<$Name.$$CurrentPath>", 0
		ALIGN


;*******************************************************************************
;----f- Director.s.Memoriser.MemoriseFile
; Name
;   MemoriseFile
;
; Purpose
;   This memorises the string pointed to by r0.
;   This is almost certainly called from a filter, so should be careful
;   if the memoriser menu is being displayed?
;
; Entry
;   r0  path
;
; Exit
;   It may return errors
;------
;*******************************************************************************


MemoriseFile	ROUTINE	"r0-r9"

		LDR	lr, MaxMemoriserItems
		CMP	lr, #0
		BEQ	exit$l

		MOV	r7, r0				; r7  path
		ADR	r1, WimpScrapVar		; is it |<Wimp$Scrap>?
		BL	strcmpi
		LDRNE	r1, WimpScrap			; or <Wimp$Scrap>?
		BLNE	strcmpi
		BEQ	exit$l				; end if it was

		MOV	r0, #OSFile_ReadNoPath
		MOV	r1, r7
		BL	ClaimMediaUpCalls
		SWI	XOS_File
		MOV	r4, pc				; 26-bit: store flags in r4 (uses PSR)
		MRS	r4, CPSR			; 32-bit: store flags in r4 (uses PSR)
		BL	ReleaseMediaUpCalls
		TST	r4, #V_bit			; if the SWI generated an error
		MOVNE	r0, #OSFile_NotFound		; error => not found
		CMP	r0, #OSFile_NotFound
		BEQ	exit$l				; end if the file did not exist

		ADR	r4, MemorisedFiles		; string block for memorised files
		MOV	r6, #0				; r6 = count of items
		LDR	r8, [r4, #StringBlock_buffer]	; r8  start of strings
		CMP	r8, #0
		BEQ	copy$l
		LDR	r9, [r4, #StringBlock_offset]
		ADD	r9, r8, r9			; r9  end of strings

loop$l		SUB	r9, r9, #1			; r9 points at null of next string
		CMP	r8, r9
		BGE	endloop$l
		BL	find_previous_string
		ADD	r6, r6, #1

		MOV	r0, r9
		MOV	r1, r7
		BL	strcmpi
		BNE	loop$l				; look for the string in the block

		MOV	r5, #0				; don't delete last item
		BL	delete_string			; Now delete the duplicated string from the block
		BEQ	exit$l				; end if string not deleted
		B	copy$l
endloop$l
		LDR	r0, MaxMemoriserItems
		CMP	r6, r0				; now check to see whether we have too many items in the menu
		BLT	copy$l

		MOV	r5, #1				; okay to delete last item
		MOV	r9, r8
		BL	delete_string			; delete the first string in the block

copy$l		MOV	r0, r7
		BL	StringCopy			; copy the string into the string block
		BVS	error$l
		ADD	r6, r6, #1			; one more string

exit$l		ClearV
		EXIT

error$l		EXIT_ERR

WimpScrapVar	DCB	"<Wimp$Scrap>", 0
		ALIGN


;*******************************************************************************
;----f- Director.s.Memoriser.MemoriserMenuCreate
; Name
;   MemoriserMenuCreate
;
; Purpose
;   This makes the memoriser menus onto the Displayed list as a temporary
;   menu
;
; Entry
;   r0 = flags for which things to put in the menu
;     Bit 0: OSFile_TypeApplication
;     Bit 1: OSFile_TypeDir or OSFile_IsImage
;     Bit 2: OSFile_IsFile
;
; Exit
;   It may return errors
;   r0  menu block
;------
;*******************************************************************************


MemoriserMenuCreate	ROUTINE	"r1-r11", EXPORT

		MVN	r10, r0, LSL #3			; r10 is flags
		AND	r10, r10, #2_111111		; xxx111 with flags, existence checking and no not found

		MOV	r0, #0				; auto menu, not named
		ADR	r1, MenuDisplayedAnchor		; list to link into
		MOV	r2, #1				; temporary menu
		ListEnd	r1, lr				; end of the list
		BL	MenuBlockCreate			; create a new menu block to r0
		BVS	error$l
		MOV	r11, r0				; r11 points to menu block

		ADR	r0, memorisername$l
		MOV	r1, #0				; no key string
		BL	MenuInitialise			; start the menu
		BVS	error$l

		ADR	r4, MemorisedFiles		; point to string block
		LDR	r8, [r4, #StringBlock_buffer]	; r8  start of strings
		LDR	r9, [r4, #StringBlock_offset]
		ADD	r9, r8, r9			; r9  end of strings
		MOV	r6, #0				; r6 = count of items
		LDR	r7, MaxMemoriserItems		; r7 = max number of items

loop$l		SUB	r9, r9, #1			; r9 points at null of next string
		CMP	r8, r9
		BGE	endloop$l
		BL	find_previous_string
		ADD	r6, r6, #1

		CMP	r6, r7				; if we have more than the maximum then delete them
		BGT	delete$l

		MOV	r0, r9				; r0  path name
		MOV	r1, #0				; r1  menu text, 0 => use leaf name of path
		MOV	r2, #0				; r2  key string
		MOV	r3, r10				; flags
		BL	MenuAddPathItem			; r0 = type, r1 = file type
		BVS	error$l

		TEQ	r0, #OSFile_NotFound		; continue if found
		SUBEQ	r6, r6, #1			; ignore the non-existant file in the count
		BNE	loop$l

		; Delete item from the strings block

delete$l	MOV	r5, #1				; okay to delete last item
		BL	delete_string			; delete the current string

		B	loop$l
endloop$l

		MOV	r0, r11				; return with MenuBlock pointer
		ClearV
		EXIT

error$l		EXIT_ERR


memorisername$l	DCB	"Memoriser", 0
		ALIGN


;*******************************************************************************
;----f- Director.s.Memoriser.Star_MemoriserItems
; Name
;   Star_MemoriserItems
;
; Purpose
;   This sets the number of items in the Memoriser Menu
;
; Entry
;
;
; Exit
;
;------
;*******************************************************************************


Help_MemoriserItems	FOREXPORT
		[	OSVersion = 310
		DCB	"This sets the number of items in the Memoriser Menu.", 13
		|
		DCB	"Help_MemoriserItems", 0
		]

Syntax_MemoriserItems	FOREXPORT
		[	OSVersion = 310
		DCB	"Syntax: *MemoriserItems <items>", 0
		|
		DCB	"Syntax_MemoriserItems", 0
		]
		ALIGN

Args_MemoriserItems	DCB	"/E/A", 0
		ALIGN

Star_MemoriserItems	FOREXPORT
		LDR	wp, [r12]
		SaveRegs
		ROUTINE_SF	NONE
scratch$l	#	0
arg_items$l	#	4
space$l		#	scratch_size - :INDEX: @
		END_SF

		MOV	r1, r0				; translate given string
		ADR	r0, Args_MemoriserItems
		ADR	r2, scratch$l
		MOV	r3, #scratch_size
		SWI	XOS_ReadArgs
		BVS	ErrorReturn

		LDR	r0, arg_items$l
		BL	read_eval
		ADD	r0, r0, r0			; Double the number of items we keep internally
		STR	r0, MaxMemoriserItems

		B	NormalReturn


;*******************************************************************************
;----f- Director.s.Memoriser.Star_MemoriserSave
; Name
;   Star_MemoriserSave
;
; Purpose
;   This saves the memoriser entries
;
; Entry
;   r0  command
;
; Exit
;
;------
;*******************************************************************************


Help_MemoriserSave	FOREXPORT
		[	OSVersion = 310
		DCB	"This saves the currently memorised files.", 13
		|
		DCB	"Help_MemoriserSave", 0
		]

Syntax_MemoriserSave	FOREXPORT
		[	OSVersion = 310
		DCB	"Syntax: *MemoriserSave <file>", 0
		|
		DCB	"Syntax_MemoriserSave", 0
		]
		ALIGN

Star_MemoriserSave	FOREXPORT
		LDR	wp, [r12]
		SaveRegs
		ROUTINE	NONE

		MOV	r10, r0				; r10  path
		LDR	r8, MemorisedFiles + StringBlock_buffer	; r8  strings
		LDR	r9, MemorisedFiles + StringBlock_offset	; r9 = length
		CMP	r9, #0				; is the length 0?
		BEQ	save$l				; if so, just save the file

		; convert control characters to line feeds

		MOV	r0, r8				;  start
		MOV	r1, r9				; length
		MOV	r3, #10				; line feed
loop$l		LDRB	r2, [r0, r1]			; contents of strings + offset
		CMP	r2, #32				; if char < 32 then
		STRLTB	r3, [r0, r1]			; write a line feed over it
		SUBS	r1, r1, #1
		CMP	r1, #-1
		BGT	loop$l

		; save the file

save$l		MOV	r0, #OSFile_SaveStamped		; = 10
		MOV	r1, r10				;   path
		MOV	r2, #OSFile_TypeText + 1	; file type = &FFF + 1
		SUB	r2, r2, #1			; file type = &FFF
		MOV	r4, r8				;  data
		ADD	r5, r8, r9			;  end
		SWI	XOS_File			; save the file
		BVS	ErrorReturn

		B	NormalReturn


;*******************************************************************************
;----f- Director.s.Memoriser.Star_MemoriserLoad
; Name
;   Star_MemoriserLoad
;
; Purpose
;   This loads memoriser entries from a file
;
; Entry
;   r0  file name
;
; Exit
;
;------
;*******************************************************************************


Help_MemoriserLoad	FOREXPORT
		[	OSVersion = 310
		DCB	"This loads the memoriser with the contents of the file.", 13
		|
		DCB	"Help_MemoriserLoad", 0
		]

Syntax_MemoriserLoad	FOREXPORT
		[	OSVersion = 310
		DCB	"Syntax: *MemoriserLoad <file>", 0
		|
		DCB	"Syntax_MemoriserLoad", 0
		]
		ALIGN

Star_MemoriserLoad	FOREXPORT
		LDR	wp, [r12]
		SaveRegs
		ROUTINE	NONE

		MOV	r10, r0				;  path

		MOV	r0, #OSFile_ReadStamped		; = 20
		MOV	r1, r10				;  path
		SWI	XOS_File
		BVS	ErrorReturn			; r4 = size
		TST	r0, #1				; file or image
		BEQ	notfound$l

		ADD	r0, r4, #1			; include space for a terminator
		ADR	r4, MemorisedFiles
		MOV	lr, #0
		STR	lr, [r4, #StringBlock_offset]	; overwrite what is in the stringblock
		BL	StringCreate			; make space for the file on the end
		BVS	ErrorReturn
		MOV	r8, r0				;  start of block

		MOV	r0, #OSFile_LoadStamped		; = 255
		MOV	r1, r10				;  path
		MOV	r2, r8				;  to the data
		MOV	r3, #0
		SWI	XOS_File			; load the file in
		BVS	ErrorReturn

		ADR	r4, MemorisedFiles
		LDR	r3, [r4, #StringBlock_offset]	; = used size of block
		LDR	r0, [r4, #StringBlock_buffer]	;  start of MemorisedFiles
;		MOV	r1, #0
;		STRB	r1, [r0, r3]			; 0 terminate (just in case)

		; trim control ( ASCII < 32 ) characters from the end

		SUBS	r3, r3, #1			; r3  to last character
loop$l		LDRB	r1, [r0, r3]			; load (last) character in block
		CMP	r1, #32
;		ADDPL	r3, r3, #1			;
		BPL	nonctrlchar$l			; >= 32 and exit loop
		SUBS	r3, r3, #1			; < 32 subtract 1 from the offset
		CMP	r3, #-1				; have we gone too far?
		BNE	loop$l				; if not, repeat
		MOV	r3, #0

nonctrlchar$l
;		ADD	r3, r3, #1			; set passed the char
;		MOV	r3, #0
		STR	r3, [r4, #StringBlock_offset]	; save new used size of block

		B	NormalReturn

notfound$l	ReportErrorMT	"File not found", Error_FileNotFound


;*******************************************************************************
;----f- Director.s.Memoriser.Star_MemoriserClear
; Name
;   Star_MemoriserClear
;
; Purpose
;   This clears memoriser entries
;
; Entry
;
;
; Exit
;
;------
;*******************************************************************************


Help_MemoriserClear	FOREXPORT
		[	OSVersion = 310
		DCB	"This clears the memoriser.", 13
		|
		DCB	"Help_MemoriserClear", 0
		]

Syntax_MemoriserClear	FOREXPORT
		[	OSVersion = 310
		DCB	"Syntax: *MemoriserClear", 0
		|
		DCB	"Syntax_MemoriserClear", 0
		]
		ALIGN

Star_MemoriserClear	FOREXPORT
		LDR	wp, [r12]
		SaveRegs
		ROUTINE	NONE

		MOV	r0, #0
		ADR	r4, MemorisedFiles		; =  MemorisedFiles
		MOV	lr, #0				; r14 = 0
		STR	lr, [r4, #StringBlock_offset]	; overwrite what is in the stringblock

		B	NormalReturn


;*******************************************************************************

		END
